package com.ruoyi.common.utils.ocr;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.config.properties.OcrProperties;
import com.ruoyi.common.core.domain.OcrResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

@Component
@Slf4j
public class OcrUtil {

    private final OcrProperties ocrProperties;

    public OcrUtil(OcrProperties ocrProperties) {
        this.ocrProperties = ocrProperties;
    }

    public String getQueryUrl(Map<String, String> params) {
        // 1. 统一按字典序排序
        Map<String, String> sorted = new TreeMap<>();
        Optional.ofNullable(params).ifPresent(sorted::putAll);
        sorted.put("AccessKey", ocrProperties.getAppKey());
        sorted.put("timestamp", String.valueOf(System.currentTimeMillis()));
        sorted.put("nonce", String.valueOf(ThreadLocalRandom.current().nextInt()));

        // 2. 计算签名（SecretKey 不参与最终 URL）
        String sign = sign(sorted, ocrProperties.getAppSecret());
        sorted.put("sign", sign);

        // 3. 拼 URL 参数
        return sorted.entrySet().stream()
            .map(e -> e.getKey() + "=" + urlEncode(e.getValue()))
            .collect(Collectors.joining("&", "?", ""));
    }

    /**
     * 按 k1=v1&k2=v2...&SecretKey=xxx 拼接后做 MD5 → 大写
     */
    private static String sign(Map<String, String> sorted, String secretKey) {
        String base = sorted.entrySet().stream()
            .map(e -> e.getKey() + "=" + e.getValue())
            .collect(Collectors.joining("&"))
            + "&SecretKey=" + secretKey;
        return DigestUtils.md5DigestAsHex(base.getBytes(StandardCharsets.UTF_8)).toUpperCase();
    }

    private static String urlEncode(String value) {
        try {
            return URLEncoder.encode(value, StandardCharsets.UTF_8.name());
        } catch (UnsupportedEncodingException impossible) {
            // UTF-8 必支持，不会走到这里
            throw new IllegalStateException(impossible);
        }
    }

    public OcrResult sfzOcr(Map<String, String> params, Map<String, Object> bodyParams) {
        System.out.println("===========开始ocr==========");
        String queryUrl = getQueryUrl2(new LinkedHashMap<>());
        HttpRequest post = HttpUtil.createPost(ocrProperties.getEndpoint() + ocrProperties.getUri() + queryUrl);
        post.contentType("application/json");
        post.charset("utf-8");
        post.body(JSONUtil.createObj().set("image_name", bodyParams.get("image_name")).set("image_data", bodyParams.get("image_data")).toString());
        post.header("hsPartyId", ocrProperties.getHsPartyId());
        System.out.println("===========ocr queryUrl==========" + queryUrl);
        log.warn("===========ocr queryUrl==========" + queryUrl);
        try (HttpResponse response = post.execute()) {
            String body = response.body();
            System.out.println("===========ocr body==========" + body);
            OcrResult ocrResult = JSONObject.parseObject(body, OcrResult.class);
            if (ocrResult.getResult_data() == null) {
                ocrResult.setResult_code("500");
                ocrResult.setImage_name(bodyParams.get("image_name").toString());
                ocrResult.setResult_data(body);
            } else {
                SM2Decryption sm2Decryption = new SM2Decryption(0, ocrProperties.getGPrivateKey());
                ocrResult.setResult_data(sm2Decryption.sm2Decrypt(ocrResult.getResult_data()));
            }
            return ocrResult;
        } catch (Exception e) {
            OcrResult ocrResult = new OcrResult();
            ocrResult.setResult_code("500");
            ocrResult.setImage_name(bodyParams.get("image_name").toString());
            ocrResult.setResult_data(e.getMessage());
            System.out.println("============ocr error==========" + e.getMessage());
            return ocrResult;
        }

    }

    public String getQueryUrl2(LinkedHashMap<String, String> params) {
        Map<String, String> sorted = new LinkedHashMap<>();
        if (null != params && params.size() > 0) {
            params.forEach((k, v) -> sorted.put(k, v));
        }
        sorted.put("AccessKey", ocrProperties.getAppKey());
        sorted.put("SecretKey", ocrProperties.getAppSecret());
        sorted.put("timestamp", String.valueOf(System.currentTimeMillis()));
        sorted.put("nonce", String.valueOf(new Random().nextInt()));
        sorted.put("sign", getSign2(sorted));
        sorted.remove("SecretKey");
        StringBuilder builder = new StringBuilder();
        builder.append("?");
        sorted.forEach((k, v) -> {
            try {
                builder.append(k).append("=").append(URLEncoder.encode(v, "utf-8")).append("&");
            } catch (UnsupportedEncodingException e) {

                System.out.println("对参数进行重新编码时出错。");
            }
        });
        builder.deleteCharAt(builder.length() - 1);
        return builder.toString();
    }

    //计算sign
    public String getSign2(Map<String, String> params) {
        Map<String, String> sorted = new TreeMap<>();
        params.forEach((k, v) -> sorted.put(k, v));
        StringBuilder builder = new StringBuilder();
        sorted.forEach((k, v) -> {
            builder.append(k).append("=").append(v).append("&");

        });
        String value = builder.toString();
        value = value.substring(0, value.length() - 1);
        return DigestUtils.md5DigestAsHex(value.getBytes()).toUpperCase();
    }

    public byte[] convertJpgToCompressedJpg(MultipartFile jpgFile, float quality) throws IOException {
        if (jpgFile.isEmpty()) return "文件为空".getBytes();

        BufferedImage image = ImageIO.read(jpgFile.getInputStream());
        if (image == null) return "无法读取图像文件".getBytes();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        // 获取 JPG writer
        ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();
        ImageWriteParam param = writer.getDefaultWriteParam();
        param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
        param.setCompressionQuality(quality); // 0.0f ~ 1.0f

        writer.setOutput(ImageIO.createImageOutputStream(baos));
        writer.write(null, new IIOImage(image, null, null), param);
        writer.dispose();

        return baos.toByteArray();
    }

    public byte[] compressPng(MultipartFile file) throws IOException {
        BufferedImage image = ImageIO.read(file.getInputStream());

        // Create output stream
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

        // Set compression parameters (for PNG)
        ImageWriter writer = ImageIO.getImageWritersByFormatName("png").next();
        ImageWriteParam writeParam = writer.getDefaultWriteParam();
        ImageOutputStream output = ImageIO.createImageOutputStream(outputStream);
        writer.setOutput(output);

        // Write the compressed image
        writer.write(null, new IIOImage(image, null, null), writeParam);

        return outputStream.toByteArray();
    }

    public byte[] convertJpgToPngBytes(MultipartFile jpgFile) {
        BufferedImage image = null;
        try {
            image = ImageIO.read(jpgFile.getInputStream());
            ByteArrayOutputStream pngOutputStream = new ByteArrayOutputStream();
            ImageIO.write(image, "png", pngOutputStream);
            return pngOutputStream.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public byte[] convertAndCompressJpgToPng(MultipartFile jpgFile, float compressionQuality) {
        try {
            // 1. Read the JPG image
            BufferedImage image = ImageIO.read(jpgFile.getInputStream());

            // 2. Create output stream for PNG
            ByteArrayOutputStream pngOutputStream = new ByteArrayOutputStream();

            // 3. Use advanced ImageWriter for compression control
            Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("png");
            if (!writers.hasNext()) {
                throw new IllegalStateException("No PNG ImageWriter found");
            }

            ImageWriter writer = writers.next();
            ImageWriteParam writeParam = writer.getDefaultWriteParam();

            // 4. For PNG, compression is lossless but we can optimize
            if (writeParam.canWriteCompressed()) {
                writeParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                // Note: For PNG, compressionQuality affects zlib compression level
                // 1.0f = best speed, 0.0f = best compression
                writeParam.setCompressionQuality(compressionQuality);
            }

            // 5. Write the optimized PNG
            ImageOutputStream output = ImageIO.createImageOutputStream(pngOutputStream);
            writer.setOutput(output);
            writer.write(null, new IIOImage(image, null, null), writeParam);
            writer.dispose();
            output.close();

            return pngOutputStream.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException("Image conversion failed", e);
        }
    }
}
